package cn.tannn.cat.file.server.init;

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 动态管理文件映射 - 本地存储处理器
 */
@Component
@Slf4j
public class LocalInitDynamicResourceHandlerManager implements WebMvcConfigurer {
    public static final String DEFAULT_NAME = "mappingPath";

    @Autowired
    private ResourceLoader resourceLoader;

    private final Map<String, ResourceHandlerConfig> dynamicConfigs = new ConcurrentHashMap<>();

    /**
     * 注册动态资源拦截器
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new DynamicResourceInterceptor()).addPathPatterns("/**");
    }

    /**
     * 动态新增资源处理器（存在则覆盖）
     */
    public synchronized void addResourceHandler(String name, String pathPattern, String... locations) {
        try {
            ResourceHandlerConfig config = new ResourceHandlerConfig(pathPattern, locations);
            dynamicConfigs.put(name, config);
            log.info("Successfully added resource handler: {} -> {}", name, pathPattern);
        } catch (Exception e) {
            log.error("Failed to add resource handler: {}, error: {}", name, e.getMessage());
            throw new RuntimeException("Failed to add resource handler: " + name, e);
        }
    }

    /**
     * 动态追加资源处理器
     */
    public synchronized void appendResourceHandler(String name, String pathPattern, String... newLocations) {
        try {
            ResourceHandlerConfig existingConfig = dynamicConfigs.get(name);
            if (existingConfig != null && existingConfig.getPathPattern().equals(pathPattern)) {
                // 合并现有和新的 locations 并去重
                Set<String> locationSet = new LinkedHashSet<>();
                Collections.addAll(locationSet, existingConfig.getLocations());
                Collections.addAll(locationSet, newLocations);

                // 更新配置
                existingConfig.setLocations(locationSet.toArray(new String[0]));
                log.info("Successfully appended locations to resource handler: {} -> {}", name, pathPattern);
            } else {
                // 如果不存在或 pathPattern 不同，创建新配置
                ResourceHandlerConfig config = new ResourceHandlerConfig(pathPattern, newLocations);
                dynamicConfigs.put(name, config);
                log.info("Successfully created new resource handler: {} -> {}", name, pathPattern);
            }
        } catch (Exception e) {
            log.error("Failed to append resource handler: {}, error: {}", name, e.getMessage());
            throw new RuntimeException("Failed to append resource handler: " + name, e);
        }
    }

    /**
     * 动态移除资源处理器
     */
    public synchronized void removeResourceHandler(String name) {
        if (dynamicConfigs.remove(name) != null) {
            log.warn("Successfully removed resource handler: {}", name);
        }
    }

    /**
     * 动态移除资源处理器
     */
    /**
     * 动态移除资源处理器中的指定位置
     *
     * @param name 处理器名称
     * @param pathPattern 路径模式
     * @param location 要移除的位置
     * @return true 如果移除成功，false 如果处理器不存在或路径不匹配
     */
    public synchronized boolean removeResourceHandler(String name, String pathPattern, String location) {
        try {
            ResourceHandlerConfig existingConfig = dynamicConfigs.get(name);
            if (existingConfig != null && existingConfig.getPathPattern().equals(pathPattern)) {
                // 构建新的位置列表，排除要移除的位置
                List<String> currentLocations = new ArrayList<>(Arrays.asList(existingConfig.getLocations()));
                boolean removed = currentLocations.remove(location);

                if (removed) {
                    if (currentLocations.isEmpty()) {
                        // 如果没有剩余位置，删除整个处理器
                        dynamicConfigs.remove(name);
                        log.info("移除空资源处理器: {}", name);
                    } else {
                        // 更新处理器的位置
                        existingConfig.setLocations(currentLocations.toArray(new String[0]));
                        log.info("从资源处理器 {} 中移除位置: {}", name, location);
                    }
                    return true;
                }
            }
            return false;
        } catch (Exception e) {
            log.error("移除资源处理器位置失败: {}, error: {}", name, e.getMessage());
            throw new RuntimeException("移除资源处理器位置失败: " + name, e);
        }
    }

    /**
     * 更新资源处理器
     */
    public void updateResourceHandler(String name, String pathPattern, String... locations) {
        addResourceHandler(name, pathPattern, locations);
    }

    /**
     * 获取所有动态资源处理器信息
     */
    public Map<String, String> getAllResourceHandlers() {
        Map<String, String> result = new HashMap<>();
        dynamicConfigs.forEach((name, config) -> {
            result.put(name, config.getPathPattern() + " -> " + Arrays.toString(config.getLocations()));
        });
        return result;
    }

    /**
     * 清空所有动态资源处理器
     */
    public void clearAllResourceHandlers() {
        dynamicConfigs.clear();
    }

    /**
     * 批量添加资源处理器
     */
    public void addResourceHandlers(Map<String, ResourceHandlerConfig> configs) {
        configs.forEach((name, config) -> {
            if (config.isEnabled()) {
                addResourceHandler(name, config.getPathPattern(), config.getLocations());
            }
        });
    }

    /**
     * 动态资源拦截器
     */
    private class DynamicResourceInterceptor implements HandlerInterceptor {

        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            String requestURI = request.getRequestURI();
            String contextPath = request.getContextPath();

            // 移除context path
            if (StringUtils.hasText(contextPath)) {
                requestURI = requestURI.substring(contextPath.length());
            }

            // 检查是否匹配动态资源处理器
            for (ResourceHandlerConfig config : dynamicConfigs.values()) {
                if (config.isEnabled() && matchesPattern(requestURI, config.getPathPattern())) {
                    return handleResourceRequest(request, response, requestURI, config);
                }
            }

            return true; // 继续处理链
        }

        /**
         * 检查URL是否匹配模式
         */
        private boolean matchesPattern(String requestURI, String pattern) {
            // 简单的模式匹配，支持 /** 通配符
            if (pattern.endsWith("/**")) {
                String prefix = pattern.substring(0, pattern.length() - 3);
                return requestURI.startsWith(prefix);
            } else if (pattern.endsWith("/*")) {
                String prefix = pattern.substring(0, pattern.length() - 2);
                return requestURI.startsWith(prefix) && !requestURI.substring(prefix.length()).contains("/");
            } else {
                return requestURI.equals(pattern);
            }
        }

        /**
         * 处理资源请求
         */
        private boolean handleResourceRequest(HttpServletRequest request, HttpServletResponse response,
                                              String requestURI, ResourceHandlerConfig config) throws IOException {
            try {
                String resourcePath = extractResourcePath(requestURI, config.getPathPattern());

                // 尝试从配置的位置中找到资源
                Resource resource = null;
                for (String location : config.getLocations()) {
                    String fullPath = location;
                    if (!fullPath.endsWith("/")) {
                        fullPath += "/";
                    }
                    fullPath += resourcePath;

                    resource = resourceLoader.getResource(fullPath);
                    if (resource.exists() && resource.isReadable()) {
                        break;
                    }
                    resource = null;
                }

                if (resource != null && resource.exists()) {
                    // 设置响应头
                    setResponseHeaders(response, resource, resourcePath);

                    // 写入资源内容
                    try (InputStream inputStream = resource.getInputStream();
                         OutputStream outputStream = response.getOutputStream()) {

                        byte[] buffer = new byte[8192];
                        int bytesRead;
                        while ((bytesRead = inputStream.read(buffer)) != -1) {
                            outputStream.write(buffer, 0, bytesRead);
                        }
                        outputStream.flush();
                    }

                    return false; // 停止处理链，请求已处理
                }
            } catch (Exception e) {
                log.error("Error handling resource request: {}", e.getMessage());
            }

            return true; // 继续处理链
        }

        /**
         * 从请求URI中提取资源路径
         */
        private String extractResourcePath(String requestURI, String pattern) {
            if (pattern.endsWith("/**")) {
                String prefix = pattern.substring(0, pattern.length() - 3);
                if (requestURI.startsWith(prefix)) {
                    return requestURI.substring(prefix.length());
                }
            } else if (pattern.endsWith("/*")) {
                String prefix = pattern.substring(0, pattern.length() - 2);
                if (requestURI.startsWith(prefix)) {
                    return requestURI.substring(prefix.length());
                }
            }
            return requestURI;
        }

        /**
         * 设置响应头
         */
        private void setResponseHeaders(HttpServletResponse response, Resource resource, String resourcePath) {
            try {
                // 设置Content-Type
                String contentType = getContentType(resourcePath);
                if (contentType != null) {
                    response.setContentType(contentType);
                }

                // 设置Content-Length
                long contentLength = resource.contentLength();
                if (contentLength > 0 && contentLength <= Integer.MAX_VALUE) {
                    response.setContentLength((int) contentLength);
                }else {
                    // 强制浏览器尝试预览（而非下载）
                    response.setHeader("Content-Disposition", "inline");
                    // 支持 Range 请求（流式加载大文件）
                    response.setHeader("Accept-Ranges", "bytes");
                }

                // 设置缓存头
                response.setHeader("Cache-Control", "max-age=3600");

                // 设置Last-Modified
                long lastModified = resource.lastModified();
                if (lastModified > 0) {
                    response.setDateHeader("Last-Modified", lastModified);
                }

            } catch (IOException e) {
                log.error("Error setting response headers: {}", e.getMessage());
            }
        }

        /**
         * 根据文件扩展名获取Content-Type
         */
        private String getContentType(String resourcePath) {
            String extension = getFileExtension(resourcePath);
            if (extension != null) {
                return switch (extension.toLowerCase()) {
                    case "html" -> MediaType.TEXT_HTML_VALUE;
                    case "css" -> "text/css";
                    case "js" -> "application/javascript";
                    case "json" -> MediaType.APPLICATION_JSON_VALUE;
                    case "png" -> MediaType.IMAGE_PNG_VALUE;
                    case "jpg", "jpeg" -> MediaType.IMAGE_JPEG_VALUE;
                    case "gif" -> MediaType.IMAGE_GIF_VALUE;
                    case "svg" -> "image/svg+xml";
                    case "ico" -> "image/x-icon";
                    case "pdf" -> MediaType.APPLICATION_PDF_VALUE;
                    case "txt" -> MediaType.TEXT_PLAIN_VALUE;
                    case "xml" -> MediaType.APPLICATION_XML_VALUE;

                    // 视频文件
                    case "mp4" -> "video/mp4";
                    case "avi" -> "video/x-msvideo";
                    case "mov" -> "video/quicktime";
                    case "mkv" -> "video/x-matroska";
                    case "flv" -> "video/x-flv";
                    case "webm" -> "video/webm";

                    // 音频文件
                    case "mp3" -> "audio/mpeg";
                    case "wav" -> "audio/wav";
                    case "aac" -> "audio/aac";
                    case "ogg" -> "audio/ogg";

                    // 压缩文件
                    case "zip" -> "application/zip";
                    case "rar" -> "application/x-rar-compressed";
                    case "tar" -> "application/x-tar";
                    case "gz" -> "application/gzip";

                    // 文档文件
                    case "csv" -> "text/csv";
                    case "xls" -> "application/vnd.ms-excel";
                    case "xlsx" -> "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
                    case "doc" -> "application/msword";
                    case "docx" -> "application/vnd.openxmlformats-officedocument.wordprocessingml.document";
                    case "ppt" -> "application/vnd.ms-powerpoint";
                    case "pptx" -> "application/vnd.openxmlformats-officedocument.presentationml.presentation";

                    // 开发相关文件
                    case "log" -> MediaType.TEXT_PLAIN_VALUE; // 日志文件
                    case "yml" -> "application/x-yaml"; // YAML 配置文件
                    case "yaml" -> "application/x-yaml"; // YAML 配置文件
                    case "properties" -> MediaType.TEXT_PLAIN_VALUE; // Java 属性文件
                    case "sh" -> MediaType.TEXT_PLAIN_VALUE; // Shell 脚本文件
                    case "bat" -> MediaType.TEXT_PLAIN_VALUE; // Windows 批处理文件
                    case "sql" -> MediaType.TEXT_PLAIN_VALUE; // SQL 文件
                    case "conf" -> MediaType.TEXT_PLAIN_VALUE; // 配置文件
                    case "ini" -> MediaType.TEXT_PLAIN_VALUE; // INI 配置文件
                    case "md" -> MediaType.TEXT_PLAIN_VALUE; // Markdown 文件
                    case "gitignore" -> MediaType.TEXT_PLAIN_VALUE; // .gitignore 文件
                    case "editorconfig" -> MediaType.TEXT_PLAIN_VALUE; // .editorconfig 文件

                    default -> MediaType.APPLICATION_OCTET_STREAM_VALUE;
                };
            }
            return MediaType.APPLICATION_OCTET_STREAM_VALUE;
        }

        /**
         * 获取文件扩展名
         */
        private String getFileExtension(String filename) {
            if (filename == null || filename.isEmpty()) {
                return null;
            }
            int lastDotIndex = filename.lastIndexOf('.');
            if (lastDotIndex > 0 && lastDotIndex < filename.length() - 1) {
                return filename.substring(lastDotIndex + 1);
            }
            return null;
        }
    }

    /**
     * 资源处理器配置类
     */
    public static class ResourceHandlerConfig {
        private String pathPattern;
        private String[] locations;
        private boolean enabled = true;
        private int order = 0;

        public ResourceHandlerConfig() {}

        public ResourceHandlerConfig(String pathPattern, String[] locations) {
            this.pathPattern = pathPattern;
            this.locations = locations;
        }

        // Getters and Setters
        public String getPathPattern() { return pathPattern; }
        public void setPathPattern(String pathPattern) { this.pathPattern = pathPattern; }

        public String[] getLocations() { return locations; }
        public void setLocations(String[] locations) { this.locations = locations; }

        public boolean isEnabled() { return enabled; }
        public void setEnabled(boolean enabled) { this.enabled = enabled; }

        public int getOrder() { return order; }
        public void setOrder(int order) { this.order = order; }
    }
}
